package com.sz.biz.pay.transfer.service.impl;

import com.sz.biz.common.constants.ErrorCodes;
import com.sz.biz.common.constants.ModuleNames;
import com.sz.biz.common.utils.NOGenerator;
import com.sz.biz.pay.account.entity.Account;
import com.sz.biz.pay.account.entity.BankCard;
import com.sz.biz.pay.account.entity.ThirdPayAccount;
import com.sz.biz.pay.account.entity.Transaction;
import com.sz.biz.pay.account.service.AccountService;
import com.sz.biz.pay.account.service.BankCardService;
import com.sz.biz.pay.account.service.ThirdPayAccountService;
import com.sz.biz.pay.account.service.TransactionService;
import com.sz.biz.pay.constants.PayAction;
import com.sz.biz.pay.constants.PayConstant;
import com.sz.biz.pay.transfer.dto.ConfirmedDto;
import com.sz.biz.pay.transfer.dto.RechargeDto;
import com.sz.biz.pay.transfer.dto.WithdrawalsDto;
import com.sz.biz.pay.transfer.service.RechargeWithdrawalsService;
import com.sz.biz.pay.util.BackDataUtil;
import com.sz.biz.pay.util.PayMessageTemplateUtil;
import com.sz.common.base.constants.UserActions;
import com.sz.common.base.dto.ResultDto;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.service.AbstractService;
import com.sz.common.core.service.PrincipalLogUtils;
import com.sz.common.core.service.PrincipalUtils;
import com.sz.common.core.system.service.SysSmsService;
import com.sz.common.pay.common.DateGenerator;
import com.sz.common.pay.common.SequenceGenerator;
import com.sz.common.pay.pingan.constant.Constant;
import com.sz.common.pay.pingan.core.client.PAPay;
import com.sz.common.pay.pingan.entity.b2b.rechargewithdrawal.*;
import com.sz.common.pay.pingan.exception.NoAvailableConnectException;
import com.sz.common.pay.pingan.exception.OperationFailedException;
import com.sz.common.pay.pingan.exception.PayCommonException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * Function:帐户提现与充值<br>
 *
 * @author: siqishangshu <br>
 * @date: 2017-12-04 19:08:00
 */
@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class RechargeWithdrawalsServiceImpl extends AbstractService implements RechargeWithdrawalsService {


    @Autowired
    TransactionService transactionService;
    @Autowired
    AccountService accountService;
    @Autowired
    ThirdPayAccountService thirdPayAccountService;
    @Autowired
    BankCardService bankCardService;
    @Autowired
    SysSmsService sysSmsService;

    @Value("${pay.sms.templatecode}")
    private String smsTemplateCode;

    @Override
    public ResultDto recharge(RechargeDto rechargeDto) {
        ResultDto resultDto = new ResultDto();
        String thirdLogNo = SequenceGenerator.getThirdLogNo();

        RechargeRequest request = new RechargeRequest();
        ThirdPayAccount thirdPayAccount = thirdPayAccountService.select(rechargeDto.getThirdCustId());
        BankCard bankCard = bankCardService.getByCardNo(rechargeDto.getBankNo());
        request.setSupAcctId(Constant.SUP_ACCT_ID);
        request.setCustAcctId(thirdPayAccount.getThirdPayId());
        request.setThirdCustId(thirdPayAccount.getAccountId());
        request.setIdType(bankCard.getIdType());
        request.setIdCode(bankCard.getIdCode());
        request.setTranAmount(rechargeDto.getTranAmount());
        request.setInAcctId(rechargeDto.getBankNo());
        request.setInAcctId(rechargeDto.getBankName());
        request.setCcyCode(rechargeDto.getCcyCode());
        request.setReserve(rechargeDto.getNote());
        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_PAY, UserActions.REQUEST, PayAction.RECHARGE,
                BackDataUtil.makeMap(thirdLogNo, request));
        Object object = null;
        Transaction transaction = rechargeTransaction(thirdLogNo, rechargeDto, thirdPayAccount);
        rechargeDto.setLogNo(transaction.getId());
        rechargeDto.setTime(new Date());
        try {
            RechargeResponse response = (RechargeResponse)
                    PAPay.sendMessage(thirdLogNo, request, RechargeResponse.class);
            object = response;
            resultDto.setData(Boolean.TRUE);
            transaction.setThirdBillNo(response.getFrontLogNo());
            // TODO need 3zang confirmed
            updateTranscation(transaction, PayConstant.TranStatus.SUCCESS);
            sysSmsService.createSMS(bankCard.getMobile(), PayMessageTemplateUtil.makeMessage(transaction), smsTemplateCode);
        } catch (NoAvailableConnectException e) {
            object = e.getMessage();
            transaction.setDescribeInfo(e.getMessage());
            updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_AVAILABLE_CONNECT);
        } catch (OperationFailedException e) {
            object = e.getErrorMessage();
            resultDto.setErrorCode(ErrorCodes.ERROR_PAY_OPERATION.getCode());
            resultDto.setMsg(e.getErrorMessage().getRspCode() + " " + e.getErrorMessage().getRspMsg());
            resultDto.setData(Boolean.FALSE);
            transaction.setDescribeInfo(e.getErrorMessage().getRspCode() + ":" + e.getErrorMessage().getRspMsg());
            updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
        } catch (PayCommonException e) {
            object = e.getMessage();
            transaction.setDescribeInfo(e.getMessage());
            updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_COMMON);
        } finally {
            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_PAY, UserActions.RESPONSE, PayAction.RECHARGE,
                    BackDataUtil.makeMap(thirdLogNo, object));
            rechargeDto.setStatus(transaction.getTranStatus());
        }
        return resultDto;
    }


    private Transaction rechargeTransaction(String thirdLogNo, RechargeDto rechargeDto, ThirdPayAccount thirdPayAccount) {
        Transaction transaction = new Transaction();

        transaction.setId(NOGenerator.getTranNo(PayConstant.TranscationType.RECHARGE));
        transaction.setThirdLogNo(thirdLogNo);
        transaction.setTranStatus(PayConstant.TranStatus.NEED_CONFIRMED);
        transaction.setCreateTime(new Date());
        transaction.setTranType(PayConstant.TranscationType.RECHARGE);
        transaction.setThirdType(PayConstant.PaymentType.PINGAN);
        transaction.setNote(rechargeDto.getNote());
        transaction.setCreateUserId(PrincipalUtils.getAccountId());
        transaction.setCurrency(rechargeDto.getCcyCode());
        transaction.setHandFee(PayConstant.HAND_FEE_FREE);
        transaction.setTranAmount(Double.parseDouble(rechargeDto.getTranAmount()));
        Account account = accountService.select(thirdPayAccount.getAccountId());

        transaction.setOutAccountId(rechargeDto.getBankNo());
        transaction.setOutAccountName(rechargeDto.getBankName());

        transaction.setInAccountBalance(
                bankCardService.getAccountBalance(thirdPayAccount.getThirdPayId()));
        transaction.setInAccountId(account.getId());
        transaction.setInAccountName(account.getCustName());
        transaction.setInThirdPayAccountId(thirdPayAccount.getThirdPayId());
        transaction.setInThirdPayAccountName(thirdPayAccount.getThirdPayName());
        transaction.setIsDeleted(false);
        transaction.setTranDate(DateGenerator.getyyyyMMdd());
        transaction.setTranTime(DateGenerator.getHHmmss());
        transactionService.insert(transaction);
        return transaction;
    }


    /**
     * 确认提现已经完成
     *
     * @param transaction
     * @param status
     */
    private void updateTranscation(Transaction transaction, int status) {
        transaction.setTranStatus(status);
        transactionService.update(transaction);
    }

    /**
     * 记录提现流水
     *
     * @param thirdLogNo
     * @param withdrawalsDto
     * @return
     */
    private Transaction withdrawalsTranscation(String thirdLogNo, WithdrawalsDto withdrawalsDto) {
        Transaction transaction = new Transaction();
        transaction.setId(NOGenerator.getTranNo(PayConstant.TranscationType.WITHDRAWAL));
        transaction.setThirdLogNo(thirdLogNo);
        transaction.setTranStatus(PayConstant.TranStatus.NEED_CONFIRMED);
        transaction.setCreateTime(new Date());
        transaction.setCreateUserId(PrincipalUtils.getAccountId());
        transaction.setCurrency(withdrawalsDto.getCcyCode());
        transaction.setHandFee(Double.parseDouble(withdrawalsDto.getHandFee()));
        transaction.setTranAmount(Double.parseDouble(withdrawalsDto.getTranAmount()));

        Account account = accountService.select(withdrawalsDto.getThirdCustId());
        if (account != null) {
            transaction.setOutAccountId(account.getId());
            transaction.setOutAccountName(account.getCustName());
        }
        transaction.setOutAccountBalance(
                bankCardService.getAccountBalance(withdrawalsDto.getCustAcctId()));

        ThirdPayAccount thirdPayAccount = thirdPayAccountService.findByCustAcctId(withdrawalsDto.getCustAcctId());
        if (thirdPayAccount != null) {
            transaction.setOutThirdPayAccountId(thirdPayAccount.getThirdPayId());
            transaction.setOutThirdPayAccountName(thirdPayAccount.getThirdPayName());
        }
        transaction.setTranType(PayConstant.TranscationType.WITHDRAWAL);
        transaction.setThirdType(PayConstant.PaymentType.PINGAN);
        transaction.setNote(withdrawalsDto.getNote());
        transaction.setInAccountId(withdrawalsDto.getOutAcctId());
        transaction.setInAccountName(withdrawalsDto.getOutAcctIdName());
        transaction.setIsDeleted(false);
        transaction.setTranDate(DateGenerator.getyyyyMMdd());
        transaction.setTranTime(DateGenerator.getHHmmss());
        transactionService.insert(transaction);
        return transaction;
    }

    /**
     * 获取需要签名的数据
     *
     * @param withdrawalDto
     * @return
     */
    @Override
    public ResultDto withdrawals(WithdrawalsDto withdrawalDto) {
        ResultDto resultDto = new ResultDto();
        String thirdLogNo = SequenceGenerator.getThirdLogNo();
        BankCard bankCard = bankCardService.getByCardNo(withdrawalDto.getOutAcctId());
        WithdrawalFromTradingNetworkRequest request = new WithdrawalFromTradingNetworkRequest();
        request.setTranWebName(Constant.TRAN_WEB_NAME);
        request.setThirdCustId(withdrawalDto.getThirdCustId());
        request.setIdType(bankCard.getIdType());
        request.setIdCode(bankCard.getIdCode());
        request.setTranType(Constant.TRAN_OUT_TYPE);
        request.setCustAcctId(bankCard.getCardNo());
        request.setCustName(withdrawalDto.getCustName());
        request.setSupAcctId(Constant.SUP_ACCT_ID);
        if (bankCard.getBank().contains(PayConstant.BankName.PINGAN)) {
            request.setTranType(PayConstant.TranType.INNER);
        } else {
            request.setTranType(PayConstant.TranType.OUTER);
        }
        request.setOutAcctId(withdrawalDto.getCustAcctId());
        request.setOutAcctIdName(withdrawalDto.getOutAcctIdName());
        request.setOutAcctIdBankName(bankCard.getBank());
        request.setOutAcctIdBankCode(bankCard.getBankCode());
        request.setAddress(bankCard.getAddress());
        request.setCcyCode(withdrawalDto.getCcyCode());
        request.setTranAmount(withdrawalDto.getTranAmount());
        request.setFeeOutCustId(withdrawalDto.getHandFee());
        request.setReserve(withdrawalDto.getNote());
        Transaction transaction = withdrawalsTranscation(thirdLogNo, withdrawalDto);
        withdrawalDto.setLogNo(transaction.getId());
        withdrawalDto.setStatus(transaction.getTranStatus());
        withdrawalDto.setTime(new Date());
        Object object = null;
        try {
            WithdrawalFromTradingNetworkResponse response = (WithdrawalFromTradingNetworkResponse)
                    PAPay.sendMessage(thirdLogNo, request, WithdrawalFromTradingNetworkResponse.class);
            response.setFrontLogNo(thirdLogNo);
            resultDto.setData(Boolean.TRUE);
            object = response;
            // TODO need bank confirmed
            updateTranscation(transaction, PayConstant.TranStatus.SUCCESS);
            sysSmsService.createSMS(bankCard.getMobile(), PayMessageTemplateUtil.makeMessage(transaction), smsTemplateCode);
        } catch (NoAvailableConnectException e) {
            object = e.getMessage();
            transaction.setDescribeInfo(e.getMessage());
            updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_AVAILABLE_CONNECT);
        } catch (OperationFailedException e) {
            object = e.getErrorMessage();
            resultDto.setData(Boolean.FALSE);
            resultDto.setErrorCode(ErrorCodes.ERROR_PAY_OPERATION.getCode());
            resultDto.setMsg(e.getErrorMessage().getRspCode() + ":" + e.getErrorMessage().getRspMsg());
            transaction.setDescribeInfo(e.getErrorMessage().getRspCode() + ":" + e.getErrorMessage().getRspMsg());
            updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
        } catch (PayCommonException e) {
            object = e.getMessage();
            transaction.setDescribeInfo(e.getMessage());
            updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_COMMON);
        } catch (Exception e) {
            object = e.getMessage();
            transaction.setDescribeInfo(e.getMessage());
            resultDto.setData(Boolean.FALSE);
            resultDto.setErrorCode(ErrorCodes.ERROR_PAY_OPERATION.getCode());
            resultDto.setMsg(e.getMessage());
            updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_COMMON);
        } finally {
            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_PAY, UserActions.RESPONSE, PayAction.WITHDRAWAL,
                    BackDataUtil.makeMap(thirdLogNo, object));
            withdrawalDto.setStatus(transaction.getTranStatus());
        }
        return resultDto;
    }


    /**
     * 计算手续费
     *
     * @param withdrawalsDto
     * @return
     */
    @Override
    public ResultDto withdrawalsHandFee(WithdrawalsDto withdrawalsDto) {
        ResultDto resultDto = new ResultDto();
        // TODO 平台提现收取手续费 目前为 0
        resultDto.setData(PayConstant.HAND_FEE_FREE);
        return resultDto;
    }

    @Override
    public ResultDto withdrawalsConfirmed(ConfirmedDto confirmed) {
        ResultDto resultDto = new ResultDto();
        Transaction transaction = transactionService.select(confirmed.getTransactionId());
        String thirdLogNo = transaction.getThirdLogNo();
        if (null == transaction) {
            resultDto.setData(Boolean.FALSE);
            return resultDto;
        }
        if (!transaction.getTranType().equals(PayConstant.TranscationType.WITHDRAWAL)) {
            resultDto.setData(Boolean.FALSE);
            return resultDto;
        }
        if (transaction.getTranStatus().equals(PayConstant.TranStatus.SUCCESS)) {
            resultDto.setData(Boolean.TRUE);
            return resultDto;
        }
        if (transaction.getTranStatus().equals(PayConstant.TranStatus.FAILURE)) {
            resultDto.setData(Boolean.FALSE);
            return resultDto;
        }

        if (transaction.getTranStatus().equals(PayConstant.TranStatus.NEED_CONFIRMED)) {
            BankCard bankCard = bankCardService.getByCardNo(transaction.getInAccountId());
            WithdrawalConfirmRequest request = new WithdrawalConfirmRequest();
            request.setTranWebName(Constant.TRAN_WEB_NAME);
            request.setFrontLogNo(thirdLogNo);
            request.setThirdCustId(transaction.getOutAccountId());
            request.setIdCode(bankCard.getIdCode());
            request.setIdType(bankCard.getIdType());
            request.setCustAcctId(transaction.getOutThirdPayAccountId());
            request.setCustName(transaction.getOutAccountName());
            request.setSupAcctId(Constant.SUP_ACCT_ID);
            request.setTranStatus(confirmed.getTranStatus());
            request.setReserve(confirmed.getNote());
            if (bankCard.getBank().contains(PayConstant.BankName.PINGAN)) {
                request.setTranType(PayConstant.TranType.INNER);
            } else {
                request.setTranType(PayConstant.TranType.OUTER);
            }
            request.setOutAcctId(transaction.getInAccountId());
            request.setOutAcctIdName(transaction.getInAccountName());
            request.setCcyCode(transaction.getCurrency());
            request.setTranAmount(transaction.getTranAmount() + "");
            request.setReserve(transaction.getReserve());
            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_PAY, UserActions.REQUEST, PayAction.WITHDRAWAL,
                    BackDataUtil.makeMap(thirdLogNo, request));
            Object object = null;
            resultDto.setData(confirmed);
            try {
                WithdrawalConfirmResponse response = (WithdrawalConfirmResponse)
                        PAPay.sendMessage(thirdLogNo, request, WithdrawalConfirmResponse.class);
                resultDto.setData(Boolean.TRUE);
                object = response;
                updateTranscation(transaction, PayConstant.TranStatus.SUCCESS);
                sysSmsService.createSMS(bankCard.getMobile(), PayMessageTemplateUtil.makeMessage(transaction), smsTemplateCode);
            } catch (NoAvailableConnectException e) {
                object = e.getMessage();
                transaction.setDescribeInfo(e.getMessage());
                updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
                throw Exceptions.bizException(ErrorCodes.ERROR_PAY_AVAILABLE_CONNECT);
            } catch (OperationFailedException e) {
                object = e.getErrorMessage();
                resultDto.setData(Boolean.FALSE);
                resultDto.setErrorCode(ErrorCodes.ERROR_PAY_OPERATION.getCode());
                resultDto.setMsg(e.getErrorMessage().getRspCode() + ":" + e.getErrorMessage().getRspMsg());
                transaction.setDescribeInfo(e.getErrorMessage().getRspCode() + ":" + e.getErrorMessage().getRspMsg());
                updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
            } catch (PayCommonException e) {
                object = e.getMessage();
                transaction.setDescribeInfo(e.getMessage());
                updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
                throw Exceptions.bizException(ErrorCodes.ERROR_PAY_COMMON);
            } catch (Exception e) {
                object = e.getMessage();
                transaction.setDescribeInfo(e.getMessage());
                updateTranscation(transaction, PayConstant.TranStatus.FAILURE);
                throw Exceptions.bizException(ErrorCodes.ERROR_PAY_COMMON);
            } finally {
                PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_PAY, UserActions.RESPONSE, PayAction.WITHDRAWAL,
                        BackDataUtil.makeMap(thirdLogNo, object));
                confirmed.setTime(new Date());
                confirmed.setTranStatus(transaction.getTranStatus() + "");
                confirmed.setNote(transaction.getNote());
            }
        }
        return resultDto;
    }


}
